﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SharpSoft.WPF
{
    /// <summary>
    /// 位图辅助类
    /// </summary>
    public static class BitmapHelper
    {
        /// <summary>
        /// 获取一个图像的颜色缓存用于读取像素颜色
        /// </summary>
        /// <param name="bitmapImage"></param>
        /// <returns></returns>
        public static ColorBuffer GetColorBuffer(this BitmapSource bitmapImage)
        {
            return new ColorBuffer(bitmapImage);
        }
        /// <summary>
        /// 获取像素格式的名称
        /// </summary>
        /// <param name="pixelFormat"></param>
        /// <returns></returns>
        public static string GetPixelFormatName(PixelFormat pixelFormat)
        {
            var t = typeof(System.Windows.Media.PixelFormats);
            var props = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            // Dictionary<string, PixelFormat> dic = new Dictionary<string, PixelFormat>();
            foreach (var p in props)
            {
                if (p.PropertyType == typeof(PixelFormat))
                {
                    var v = (PixelFormat)p.GetValue(null);
                    //dic.Add(p.Name, v);
                    if (pixelFormat == v)
                    {
                        return p.Name;
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// 根据像素格式名称获取其对象
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static PixelFormat GetPixelFormatByName(string name)
        {
            var t = typeof(System.Windows.Media.PixelFormats);
            var props = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);

            var p = t.GetProperty(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            if (p != null)
            {
                return (PixelFormat)p.GetValue(null);
            }
            return new PixelFormat();
        }

        /// <summary>
        /// 获取位图中不透明颜色的占比
        /// </summary>
        /// <param name="bitmapImage">位图</param>
        /// <param name="alphaThreshold">计算透明通道的阈值</param>
        /// <returns></returns>
        public static double GetArea(this BitmapSource bitmapImage, byte alphaThreshold = 200)
        {
            var colorBuf = GetColorBuffer(bitmapImage);
            int count = 0;
            for (int x = 0; x < colorBuf.PixelWidth; x++)
            {
                for (int y = 0; y < colorBuf.PixelHeight; y++)
                {
                    var pixel = colorBuf.GetPixel(x, y);
                    if (pixel.A >= alphaThreshold)
                    {
                        count++;
                    }
                }
            }
            return count / (double)(colorBuf.PixelWidth * colorBuf.PixelHeight);
        }

        /// <summary>
        /// 异步加载图片
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static async Task<BitmapImage> LoadAsync(Uri uri, BitmapCacheOption cacheOption = BitmapCacheOption.OnLoad)
        {
            return await Task.Run<BitmapImage>(delegate
            {
                BitmapImage bmp = new BitmapImage();
                bmp.BeginInit();
                bmp.CacheOption = cacheOption;
                bmp.UriSource = uri;
                bmp.EndInit();
                bmp.Freeze();
                return bmp;
            });
        }
        /// <summary>
        /// 异步加载图片
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static async Task<BitmapImage> LoadAsync(Stream stream, BitmapCacheOption cacheOption = BitmapCacheOption.OnLoad)
        {
            return await Task.Run<BitmapImage>(delegate
            {
                BitmapImage bmp = new BitmapImage();
                bmp.BeginInit();
                bmp.CacheOption = cacheOption;
                bmp.StreamSource = stream;
                bmp.EndInit();
                bmp.Freeze();
                return bmp;
            });
        }

        /// <summary>
        /// BitmapImage转数组
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static byte[] ToByteArray(this BitmapSource bmp)
        {
            Int32 PixelHeight = bmp.PixelHeight; // 图像高度
            Int32 PixelWidth = bmp.PixelWidth;   // 图像宽度

            var bpp = bmp.Format.BitsPerPixel;
            Int32 Stride = (bpp / 8) * PixelWidth; //PixelWidth << 2;         // 扫描行跨距
            Byte[] Pixels = new Byte[PixelHeight * Stride];
            if (bmp.Format == PixelFormats.Bgr32 || bmp.Format == PixelFormats.Bgra32)
            {   // 拷贝像素数据
                bmp.CopyPixels(Pixels, Stride, 0);
            }
            else
            {   // 先进行像素格式转换，再拷贝像素数据
                bmp.CopyPixels(Pixels, Stride, 0);
                //new FormatConvertedBitmap(bmp, PixelFormats.Bgr32, null, 0).CopyPixels(Pixels, Stride, 0);
            }
            return Pixels;

        }
        /// <summary>
        /// 将图像数据写入到现有图像
        /// </summary>
        /// <param name="writeableBitmap"></param>
        /// <param name="bytes"></param>
        public static void Blit(this WriteableBitmap dst, byte[] bytes)
        {
            dst.Lock();
            //var dstints = (byte*)dst.BackBuffer.ToPointer();
            System.Runtime.InteropServices.Marshal.Copy(bytes, 0, dst.BackBuffer, bytes.Length);
            //for (int i = 0; i < bytes.Length; ++i)
            //{
            //    dstints[i] = bytes[i];
            //}
            dst.AddDirtyRect(new System.Windows.Int32Rect(0, 0, dst.PixelWidth, dst.PixelHeight));
            dst.Unlock();
        }/// <summary>
         /// 位块传输，将<see cref="Bitmap"/>中的图形数据写入<see cref="WriteableBitmap"/>。
         /// </summary>
         /// <param name="dst"></param>
         /// <param name="src"></param> 
        public unsafe static void Blit(this WriteableBitmap dst, System.Drawing.Bitmap src)
        {
            int srcw = src.Width;
            int srch = src.Height;
            var rect = new System.Drawing.Rectangle(0, 0, srcw, srch);
            int left = rect.Left < 0 ? 0 : rect.Left;
            int top = rect.Top < 0 ? 0 : rect.Top;
            int right = rect.Right > srcw ? srcw : rect.Right;
            int bottom = rect.Bottom > srch ? srch : rect.Bottom;
            if (left >= right || top >= bottom) return;

            System.Drawing.Imaging.BitmapData srcdata = src.LockBits(new System.Drawing.Rectangle(0, 0, srcw, srch), System.Drawing.Imaging.ImageLockMode.ReadOnly, src.PixelFormat);
            dst.Lock();
            var srcints = (int*)srcdata.Scan0;
            var dstints = (int*)dst.BackBuffer.ToPointer();
            for (int j = top; j < bottom; ++j)
            {
                int y = j * srcw;
                for (int i = left; i < right; ++i)
                {
                    int index = y + i;
                    dstints[index] = srcints[index];
                }
            }
            dst.AddDirtyRect(new System.Windows.Int32Rect(left, top, right - left, bottom - top));
            src.UnlockBits(srcdata);
            dst.Unlock();
        }
    }
    /// <summary>
    /// 位图的颜色缓存
    /// </summary>
    public class ColorBuffer
    {
        public ColorBuffer(BitmapSource bitmapImage)
        {
            width = bitmapImage.PixelWidth;
            height = bitmapImage.PixelHeight;
            bytesPerPixel = bitmapImage.Format.BitsPerPixel / 8;
            stride = bytesPerPixel * width;
            data = new byte[bitmapImage.PixelHeight * stride];
            bitmapImage.CopyPixels(data, stride, 0);

        }
        private int bytesPerPixel;
        private int width, height;
        private int stride;
        private byte[] data;

        public int PixelWidth { get { return width; } }
        public int PixelHeight { get { return height; } }

        /// <summary>
        /// 获取指定位置的像素颜色（左上角坐标为0,0）
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public Color GetPixel(int x, int y)
        {
            var index = (width * y + x) * bytesPerPixel;

            var alpha = data[index + 3];
            var red = data[index + 2];
            var green = data[index + 1];
            var blue = data[index];

            return Color.FromArgb(alpha, red, green, blue);
        }
    }
}
